#include "UnicodeString.h"
#include "Exceptions.h"
#include "Format.h"
#include <cstring>

namespace lhcutils
{

UnicodeString::UnicodeString(const char* cString)
	: m_qstr(cString) { }

UnicodeString::UnicodeString(const void* sourceString, const char* sourceEncodingName)
	: m_qstr(InitQString(sourceString, sourceEncodingName)) { }

QString UnicodeString::InitQString(const void* sourceString, const char* sourceEncodingName)
{
	if(std::strcmp(sourceEncodingName, encodings::Utf8) == 0)
	{
		return QString::fromUtf8(reinterpret_cast<const char*>(sourceString));
	}
	else if(std::strcmp(sourceEncodingName, encodings::Utf16) == 0)
	{
		return QString::fromUtf16(reinterpret_cast<const ushort*>(sourceString));
	}
	else if(std::strcmp(sourceEncodingName, encodings::Utf32) == 0)
	{
		return QString::fromUcs4(reinterpret_cast<const uint*>(sourceString));
	}
	else if(std::strcmp(sourceEncodingName, encodings::Ascii) == 0)
	{
		// XXX: This is sensitive to outside changes of QTextCodec::setCodecForCStrings()
		// XXX: This actually uses Latin-1, not ASCII
		return QString(reinterpret_cast<const char*>(sourceString));
	}
	else
	{
		throw NotSupportedException(Format(UnicodeString("{0} is not a supported encoding"), UnicodeString(sourceEncodingName)));
	}
}

UnicodeString::UnicodeString(const void* sourceString, size_t numBytesInSource, const char* sourceEncodingName)
	: m_qstr(InitQString(sourceString, numBytesInSource, sourceEncodingName)) { }

QString UnicodeString::InitQString(const void* sourceString, size_t numBytesInSource, const char* sourceEncodingName)
{
	if(std::strcmp(sourceEncodingName, encodings::Utf8) == 0)
	{
		return QString::fromUtf8(reinterpret_cast<const char*>(sourceString), numBytesInSource);
	}
	else if(std::strcmp(sourceEncodingName, encodings::Utf16) == 0)
	{
		return QString::fromUtf16(reinterpret_cast<const ushort*>(sourceString), numBytesInSource / 2);
	}
	else if(std::strcmp(sourceEncodingName, encodings::Utf32) == 0)
	{
		return QString::fromUcs4(reinterpret_cast<const uint*>(sourceString), numBytesInSource / 4);
	}
	else if(std::strcmp(sourceEncodingName, encodings::Ascii) == 0)
	{
		// XXX: This actually uses Latin-1, not ASCII
		return QString::fromLatin1(reinterpret_cast<const char*>(sourceString), numBytesInSource);
	}
	else
	{
		throw NotSupportedException(Format(UnicodeString("{0} is not a supported encoding"), UnicodeString(sourceEncodingName)));
	}
}

UnicodeString::UnicodeString(double num, const CultureInfo& culture)
	: m_qstr(InitQString(num, culture)) { }

QString UnicodeString::InitQString(double num, const CultureInfo& culture)
{
	return culture.m_qlocale.toString(num);
}


ByteArray UnicodeString::ToEncoding(const char* encodingName) const
{
	return ToNullTerminatedEncoding(encodingName);
}

ByteArray UnicodeString::ToNullTerminatedEncoding(const char* encodingName) const
{
	if(std::strcmp(encodingName, encodings::Utf8) == 0)
	{
		return ByteArray(m_qstr.toUtf8());
	}
	else if(std::strcmp(encodingName, encodings::Utf16) == 0)
	{
		return ByteArray(Utf16Array(m_qstr));
	}
	else if(std::strcmp(encodingName, encodings::Utf32) == 0)
	{
		return ByteArray(Utf32Array(m_qstr));
	}
	else if(std::strcmp(encodingName, encodings::Ascii) == 0)
	{
		return ByteArray(m_qstr.toLatin1());
	}
	else
	{
		throw NotSupportedException(Format(UnicodeString("{0} is not a supported encoding"), UnicodeString(encodingName)));
	}
}

ByteArray UnicodeString::ToUtf8() const
{
	return ByteArray(m_qstr.toUtf8());
}

ByteArray UnicodeString::ToUtf8NullTerminated() const
{
	return ByteArray(m_qstr.toUtf8());
}

Utf16Array UnicodeString::ToUtf16() const
{
	return Utf16Array(m_qstr);
}

Utf16Array UnicodeString::ToUtf16NullTerminated() const
{
	return Utf16Array(m_qstr);
}

ByteArray UnicodeString::ToAscii() const
{
	// XXX: This actually uses Latin-1, not ASCII
	return ByteArray(m_qstr.toLatin1());
}

ByteArray UnicodeString::ToAsciiNullTerminated() const
{
	// XXX: This actually uses Latin-1, not ASCII
	return ByteArray(m_qstr.toLatin1());
}

bool UnicodeString::StartsWith(const UnicodeString& str, StringComparison::Value comparison) const
{
	// XXX: Does not take culture into account
	if(comparison == StringComparison::CurrentCulture || comparison == StringComparison::Ordinal)
	{
		return m_qstr.startsWith(str.m_qstr, Qt::CaseSensitive);
	}
	else
	{
		return m_qstr.startsWith(str.m_qstr, Qt::CaseInsensitive);
	}
}

bool UnicodeString::StartsWith(const UnicodeString& str, CaseType::Value caseBehavior, const CultureInfo& culture) const
{
	// XXX: Does not take culture into account
	if(caseBehavior == CaseType::Sensitive)
	{
		return m_qstr.startsWith(str.m_qstr, Qt::CaseSensitive);
	}
	else
	{
		return m_qstr.startsWith(str.m_qstr, Qt::CaseInsensitive);
	}
}

bool UnicodeString::EndsWith(const UnicodeString& str, StringComparison::Value comparison = StringComparison::CurrentCulture) const
{
	// XXX: Does not take culture into account
	if(comparison == StringComparison::CurrentCulture || comparison == StringComparison::Ordinal)
	{
		return m_qstr.endsWith(str.m_qstr, Qt::CaseSensitive);
	}
	else
	{
		return m_qstr.endsWith(str.m_qstr, Qt::CaseInsensitive);
	}
}

bool UnicodeString::EndsWith(const UnicodeString& str, CaseType::Value caseBehavior, const CultureInfo& culture) const
{
	// XXX: Does not take culture into account
	if(caseBehavior == CaseType::Sensitive)
	{
		return m_qstr.endsWith(str.m_qstr, Qt::CaseSensitive);
	}
	else
	{
		return m_qstr.endsWith(str.m_qstr, Qt::CaseInsensitive);
	}
}

bool UnicodeString::EncodingSupported(const char* encodingName)
{
	return std::strcmp(encodingName, encodings::Utf8) == 0
		|| std::strcmp(encodingName, encodings::Utf16) == 0
		|| std::strcmp(encodingName, encodings::Utf32) == 0
		|| std::strcmp(encodingName, encodings::Ascii) == 0;
}
	
}

/*
 Copyright 2011 Greg Najda

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
